<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>防抖</title>
</head>

<body>
    <input type="text" id="search-input" placeholder="输入搜索内容">
    <p>防抖(Debounce) 和 节流(Throttle) 技术用于限制函数执行的次数。</p>
    <p>防抖(Debounce) 和 节流(Throttle) 是通过控制函数被调用的频率来优化脚本性能的两种方法</p>

    <p>
        防抖：无论用户触发多少次事件,对应的回调函数只会在事件停止触发指定事件后执行。
        (即：回调函数在事件停止触发指定时间后被调用)
    </p>

    <p>
        例如,假设用户在 100 毫秒内点击了 5 次按钮。防抖技术不会让这些点击中的任何一个执行对应的回调函数。
        一旦用户停止点击,如果去抖时间为 100 毫秒,则回调函数将在 100毫秒后执行。因此, 肉眼看来,
        防抖就像将多个事件组合成一个事件一样。
    </p>

    <h4>1.1 防抖函数的实现 </h4>

    <p>

        (1) 版本1 —— 停止触发事件n毫秒后执行回调函数。 <br>
        触发事件后函数不会立即执行,而是在停止事件触发后 n 毫秒后执行,
        如果在 n 毫秒内又触发了事件,则会重新计时 <br>

        (2) 版本2 —— 触发事件后立即执行回调函数, 但是触发后n毫秒内不会再执行回调函数,
        如果 n 毫秒内触发了事件,也会重新计时。
    </p>

    <h4>防抖的实际应用</h4>
    <p>
        - (1)搜索框建议项
        通常，搜索框会提供下拉菜单，为用户当前的输入提供自动完成选项。但有时建议项是通过请求后端得到的。
        可以在实现提示文本时应用防抖，在等待用户停止输入一段时间后再显示建议文本。因此，在每次击键时，
        都会等待几秒钟，然后再给出建议。<br>

        - (2)消除resize事件处理程序的抖动。
        window 触发 resize 的时候，不断的调整浏览器窗口大小会不断触发这个事件，用防抖让其只触发一次。<br>

        - (3)自动保存
        例如掘金一类的网站，都会内嵌文本编辑器，在编辑过程中会自动保存文本，防止数据丢失。
        每次保存都会与后端进行数据交互，所以可以应用防抖，在用户停止输入后一段时间内再自动保存。<br>

        - (4)手机号、邮箱等输入验证检测
        通常对于一些特殊格式的输入项，我们通常会检查格式。我们可以应用防抖
        在用户停止输入后一段时间再进行格式检测，而不是输入框中内容发生改变就检测。<br>

        - (5)在用户停止输入之前不要发出任何 Ajax 请求

    </p>


    <script>
        // 版本1 —— 停止触发事件n毫秒后执行回调函数
        // 触发事件后函数不会立即执行，而是在停止事件触发后 n 毫秒后执行，如果在 n 毫秒内又触发了事件，则会重新计时

        /** 
        * @desc 函数防抖 
        * @param func 回调函数 
        * @param delay 延迟执行毫秒数 
        */
        function debounce(func, delay) {
            let timer;  // 定时器

            return function () {
                let context = this;  // 记录 this 值,防止在回调函数中丢失
                let args = arguments;  // 函数参数

                //如果定时器存在，则清除定时器(如果没有,也没必要进行处理)
                timer ? clearTimeout(timer) : null;

                timer = setTimeout(() => {
                    // 防止 this 值变为 window
                    func.apply(context, args)
                }, delay);
            }
        }

        // 应用scroll滚动场景
        // window.addEventListener('scroll', debounce(function (event) {
        //     console.log('窗口大小改变');
        //     console.log(scrollY);
        // }, 1000));

        // 不会立即执行，点击body后1000毫秒后执行，如果在1000毫秒内又点击了,则会重新计时，计时结束再次点击才会执行函数。
        document.body.onclick = debounce(function () {
            console.log('hello')
        }, 1000)


        // 版本2 —— 触发事件后立即执行回调函数，但是触发后n毫秒内不会再执行回调函数，
        // 如果 n 毫秒内触发了事件，也会重新计时。
        // 示例：实时搜索框
        function _debounce(func, delay) {
            let timer;  // 定时器

            return function () {
                let context = this;  // 记录 this 值,防止在回调函数中丢失
                let args = arguments;  // 函数参数

                // 标识是否立即执行
                let isImmediately = !timer;

                //如果定时器存在，则清除定时器(如果没有,也没必要进行处理)
                timer ? clearTimeout(timer) : null;

                timer = setTimeout(() => {
                    timer = null;
                }, delay);

                // isImmediately 为 true 则 执行函数(即首次触发事件)
                isImmediately ? func.apply(context, args) : null;
            }
        }

        // 应用实时搜索框场景
        // // 假设这是你的搜索函数
        // function handleSearch(query) {
        //     // 执行搜索逻辑，可能涉及到网络请求或复杂的数据处理
        //     console.log('搜索:', query);
        // }
        // // 使用防抖函数包装搜索处理函数
        // const debouncedSearch = _debounce(handleSearch, 500); // 延迟500毫秒行
        // // 假设这是你的搜索框元素
        // const searchInput = document.getElementById('search-input');
        // // 为搜索框添加事件监听器
        // searchInput.addEventListener('input', function (event) {
        //     // 使用防抖函数处理搜索，而不是直接调用 handleSearch
        //     debouncedSearch(event.target.value);
        // });

        // 会立即执行，点击bosy后会立即执行，点击后的1000毫秒内不会再执行此函数，如果在1000毫秒内又点击了，则在重新计时，计时结束后执行函数。
        document.body.onclick = _debounce(function () {
            console.log('hello')
        }, 1000)

    </script>

</body>

</html>